{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<font color=\"red\">注</font>: 使用 tensorboard 可视化需要安装 tensorflow (TensorBoard依赖于tensorflow库，可以任意安装tensorflow的gpu/cpu版本)\n",
    "\n",
    "```shell\n",
    "pip install tensorflow-cpu\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-04-30T03:08:26.466719400Z",
     "start_time": "2024-04-30T03:08:17.277867200Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T03:16:04.305094Z",
     "iopub.status.busy": "2025-01-22T03:16:04.304917Z",
     "iopub.status.idle": "2025-01-22T03:16:10.430761Z",
     "shell.execute_reply": "2025-01-22T03:16:10.430132Z",
     "shell.execute_reply.started": "2025-01-22T03:16:04.305074Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/usr/local/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
      "  from .autonotebook import tqdm as notebook_tqdm\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sys.version_info(major=3, minor=10, micro=14, releaselevel='final', serial=0)\n",
      "matplotlib 3.10.0\n",
      "numpy 1.26.4\n",
      "pandas 2.2.3\n",
      "sklearn 1.6.0\n",
      "torch 2.5.1+cu124\n",
      "cuda:0\n"
     ]
    }
   ],
   "source": [
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import sklearn\n",
    "import pandas as pd\n",
    "import os\n",
    "import sys\n",
    "import time\n",
    "from tqdm.auto import tqdm\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "print(sys.version_info)\n",
    "for module in mpl, np, pd, sklearn, torch:\n",
    "    print(module.__name__, module.__version__)\n",
    "    \n",
    "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n",
    "print(device)\n",
    "\n",
    "seed = 42\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 数据准备\n",
    "\n",
    "https://www.kaggle.com/competitions/cifar-10/data\n",
    "\n",
    "```shell\n",
    "$ tree -L 1 cifar-10                                    \n",
    "cifar-10\n",
    "├── sampleSubmission.csv\n",
    "├── test\n",
    "├── train\n",
    "└── trainLabels.csv\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-04-30T03:09:27.792078300Z",
     "start_time": "2024-04-30T03:09:23.644044900Z"
    },
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T03:16:10.432568Z",
     "iopub.status.busy": "2025-01-22T03:16:10.431978Z",
     "iopub.status.idle": "2025-01-22T03:16:13.707527Z",
     "shell.execute_reply": "2025-01-22T03:16:13.706984Z",
     "shell.execute_reply.started": "2025-01-22T03:16:10.432546Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[(PosixPath('competitions/cifar-10/train/1.png'), 'frog'),\n",
      " (PosixPath('competitions/cifar-10/train/2.png'), 'truck'),\n",
      " (PosixPath('competitions/cifar-10/train/3.png'), 'truck'),\n",
      " (PosixPath('competitions/cifar-10/train/4.png'), 'deer'),\n",
      " (PosixPath('competitions/cifar-10/train/5.png'), 'automobile')]\n",
      "[(PosixPath('competitions/cifar-10/test/1.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/2.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/3.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/4.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/5.png'), 'cat')]\n",
      "50000 300000\n"
     ]
    }
   ],
   "source": [
    "from pathlib import Path\n",
    "\n",
    "DATA_DIR = Path(\".\")\n",
    "DATA_DIR1 =Path(\"competitions/cifar-10/\")\n",
    "train_lables_file = DATA_DIR / \"trainLabels.csv\"\n",
    "test_csv_file = DATA_DIR / \"sampleSubmission.csv\" #测试集模板csv文件\n",
    "train_folder = DATA_DIR1 / \"train\"\n",
    "test_folder = DATA_DIR1 / \"test\"\n",
    "\n",
    "#所有的类别\n",
    "class_names = [\n",
    "    'airplane',\n",
    "    'automobile',\n",
    "    'bird',\n",
    "    'cat',\n",
    "    'deer',\n",
    "    'dog',\n",
    "    'frog',\n",
    "    'horse',\n",
    "    'ship',\n",
    "    'truck',\n",
    "]\n",
    "\n",
    "def parse_csv_file(filepath, folder):\n",
    "    \"\"\"Parses csv files into (filename(path), label) format\"\"\"\n",
    "    results = []\n",
    "    #读取所有行\n",
    "    with open(filepath, 'r') as f:\n",
    "#         lines = f.readlines()  为什么加[1:]，可以试这个\n",
    "        #第一行不需要，因为第一行是标签\n",
    "        lines = f.readlines()[1:] \n",
    "    for line in lines:#依次去取每一行\n",
    "        image_id, label_str = line.strip('\\n').split(',')\n",
    "        image_full_path = folder / f\"{image_id}.png\"\n",
    "        results.append((image_full_path, label_str)) #得到对应图片的路径和分类\n",
    "    return results\n",
    "\n",
    "#解析对应的文件夹\n",
    "train_labels_info = parse_csv_file(train_lables_file, train_folder)\n",
    "test_csv_info = parse_csv_file(test_csv_file, test_folder)\n",
    "#打印\n",
    "import pprint\n",
    "pprint.pprint(train_labels_info[0:5])\n",
    "pprint.pprint(test_csv_info[0:5])\n",
    "print(len(train_labels_info), len(test_csv_info))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-04-30T03:09:30.583944400Z",
     "start_time": "2024-04-30T03:09:30.436028300Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T03:16:13.708555Z",
     "iopub.status.busy": "2025-01-22T03:16:13.708261Z",
     "iopub.status.idle": "2025-01-22T03:16:13.825978Z",
     "shell.execute_reply": "2025-01-22T03:16:13.825382Z",
     "shell.execute_reply.started": "2025-01-22T03:16:13.708533Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "                            filepath       class\n",
      "0  competitions/cifar-10/train/1.png        frog\n",
      "1  competitions/cifar-10/train/2.png       truck\n",
      "2  competitions/cifar-10/train/3.png       truck\n",
      "3  competitions/cifar-10/train/4.png        deer\n",
      "4  competitions/cifar-10/train/5.png  automobile\n",
      "                                filepath       class\n",
      "0  competitions/cifar-10/train/45001.png       horse\n",
      "1  competitions/cifar-10/train/45002.png  automobile\n",
      "2  competitions/cifar-10/train/45003.png        deer\n",
      "3  competitions/cifar-10/train/45004.png  automobile\n",
      "4  competitions/cifar-10/train/45005.png    airplane\n",
      "                           filepath class\n",
      "0  competitions/cifar-10/test/1.png   cat\n",
      "1  competitions/cifar-10/test/2.png   cat\n",
      "2  competitions/cifar-10/test/3.png   cat\n",
      "3  competitions/cifar-10/test/4.png   cat\n",
      "4  competitions/cifar-10/test/5.png   cat\n"
     ]
    }
   ],
   "source": [
    "# train_df = pd.DataFrame(train_labels_info)\n",
    "train_df = pd.DataFrame(train_labels_info[0:45000])\n",
    "valid_df = pd.DataFrame(train_labels_info[45000:])\n",
    "test_df = pd.DataFrame(test_csv_info)\n",
    "\n",
    "train_df.columns = ['filepath', 'class']\n",
    "valid_df.columns = ['filepath', 'class']\n",
    "test_df.columns = ['filepath', 'class']\n",
    "\n",
    "print(train_df.head())\n",
    "print(valid_df.head())\n",
    "print(test_df.head())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-04-30T03:09:40.361166200Z",
     "start_time": "2024-04-30T03:09:38.275234700Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T03:16:13.827017Z",
     "iopub.status.busy": "2025-01-22T03:16:13.826719Z",
     "iopub.status.idle": "2025-01-22T03:16:16.172470Z",
     "shell.execute_reply": "2025-01-22T03:16:16.171778Z",
     "shell.execute_reply.started": "2025-01-22T03:16:13.826995Z"
    }
   },
   "outputs": [],
   "source": [
    "from PIL import Image\n",
    "from torch.utils.data import Dataset, DataLoader\n",
    "from torchvision import transforms\n",
    "\n",
    "class Cifar10Dataset(Dataset):\n",
    "    df_map = {\n",
    "        \"train\": train_df,\n",
    "        \"eval\": valid_df,\n",
    "        \"test\": test_df\n",
    "    }\n",
    "    label_to_idx = {label: idx for idx, label in enumerate(class_names)}\n",
    "    idx_to_label = {idx: label for idx, label in enumerate(class_names)}\n",
    "    def __init__(self, mode, transform=None):\n",
    "        self.df = self.df_map.get(mode, None)\n",
    "        if self.df is None:\n",
    "            raise ValueError(\"mode should be one of train, val, test, but got {}\".format(mode))\n",
    "\n",
    "        self.transform = transform\n",
    "        \n",
    "    def __getitem__(self, index):\n",
    "        img_path, label = self.df.iloc[index]\n",
    "        img = Image.open(img_path).convert('RGB')\n",
    "        # # img 转换为 channel first\n",
    "        # img = img.transpose((2, 0, 1))\n",
    "        # transform\n",
    "        img = self.transform(img)\n",
    "        # label 转换为 idx\n",
    "        label = self.label_to_idx[label]\n",
    "        return img, label\n",
    "    \n",
    "    def __len__(self):\n",
    "        return self.df.shape[0]\n",
    "    \n",
    "IMAGE_SIZE = 32\n",
    "mean, std = [0.4914, 0.4822, 0.4465], [0.247, 0.243, 0.261]\n",
    "\n",
    "transforms_train = transforms.Compose([\n",
    "        # resize\n",
    "        transforms.Resize((IMAGE_SIZE, IMAGE_SIZE)),\n",
    "        # random rotation 40\n",
    "        transforms.RandomRotation(40),\n",
    "        # horizaontal flip\n",
    "        transforms.RandomHorizontalFlip(),\n",
    "        transforms.ToTensor(),\n",
    "        transforms.Normalize(mean, std)\n",
    "    ])\n",
    "\n",
    "transforms_eval = transforms.Compose([\n",
    "        # resize\n",
    "        transforms.Resize((IMAGE_SIZE, IMAGE_SIZE)),\n",
    "        transforms.ToTensor(),\n",
    "        transforms.Normalize(mean, std)\n",
    "    ])\n",
    "\n",
    "train_ds = Cifar10Dataset(\"train\", transforms_train)\n",
    "eval_ds = Cifar10Dataset(\"eval\", transforms_eval) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-04-30T03:09:43.545874100Z",
     "start_time": "2024-04-30T03:09:43.519888900Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T03:16:16.173757Z",
     "iopub.status.busy": "2025-01-22T03:16:16.173279Z",
     "iopub.status.idle": "2025-01-22T03:16:16.177564Z",
     "shell.execute_reply": "2025-01-22T03:16:16.176977Z",
     "shell.execute_reply.started": "2025-01-22T03:16:16.173733Z"
    }
   },
   "outputs": [],
   "source": [
    "batch_size = 64\n",
    "train_dl = DataLoader(train_ds, batch_size=batch_size, shuffle=True, num_workers=4)   \n",
    "eval_dl = DataLoader(eval_ds, batch_size=batch_size, shuffle=False, num_workers=4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-22T03:16:16.179540Z",
     "iopub.status.busy": "2025-01-22T03:16:16.179231Z",
     "iopub.status.idle": "2025-01-22T03:16:16.182576Z",
     "shell.execute_reply": "2025-01-22T03:16:16.181994Z",
     "shell.execute_reply.started": "2025-01-22T03:16:16.179519Z"
    }
   },
   "outputs": [],
   "source": [
    "# 遍历train_ds得到每张图片，计算每个通道的均值和方差\n",
    "# def cal_mean_std(ds):\n",
    "#     mean = 0.\n",
    "#     std = 0.\n",
    "#     for img, _ in ds:\n",
    "#         mean += img.mean(dim=(1, 2))\n",
    "#         std += img.std(dim=(1, 2))\n",
    "#     mean /= len(ds)\n",
    "#     std /= len(ds)\n",
    "#     return mean, std\n",
    "#\n",
    "# # 经过 normalize 后 均值为0，方差为1\n",
    "# print(cal_mean_std(train_ds))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 定义模型"
   ]
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "# .kaiming_uniform_（）函数的作用是使用Kaiming初始化方法初始化权重，该方法是一种基于LeCun的论文提出的一种权重初始化方法。其中内部参数有两个，mode和nonlinearity。mode表示权重初始化的分布，可以选择fan_in、fan_out、fan_avg三种方式。nonlinearity表示激活函数，可以选择relu、leaky_relu、elu、tanh、sigmoid等。"
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-04-30T03:33:59.691021200Z",
     "start_time": "2024-04-30T03:33:59.642042400Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T03:16:16.183523Z",
     "iopub.status.busy": "2025-01-22T03:16:16.183268Z",
     "iopub.status.idle": "2025-01-22T03:16:16.219889Z",
     "shell.execute_reply": "2025-01-22T03:16:16.219246Z",
     "shell.execute_reply.started": "2025-01-22T03:16:16.183503Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "             model.0.weight             paramerters num: 864\n",
      "              model.0.bias              paramerters num: 32\n",
      "             model.2.weight             paramerters num: 9216\n",
      "              model.2.bias              paramerters num: 32\n",
      "             model.5.weight             paramerters num: 9216\n",
      "              model.5.bias              paramerters num: 32\n",
      "             model.7.weight             paramerters num: 9216\n",
      "              model.7.bias              paramerters num: 32\n",
      "            model.10.weight             paramerters num: 9216\n",
      "             model.10.bias              paramerters num: 32\n",
      "            model.12.weight             paramerters num: 9216\n",
      "             model.12.bias              paramerters num: 32\n",
      "            model.16.weight             paramerters num: 5120\n",
      "             model.16.bias              paramerters num: 10\n"
     ]
    }
   ],
   "source": [
    "class VGG(nn.Module):\n",
    "    def __init__(self, num_classes):\n",
    "        super().__init__()\n",
    "        self.model = nn.Sequential(\n",
    "            nn.Conv2d(in_channels=3, out_channels=32, kernel_size=3, padding=\"same\"),\n",
    "            nn.ReLU(),\n",
    "            nn.Conv2d(in_channels=32, out_channels=32, kernel_size=3, padding=\"same\"),\n",
    "            nn.ReLU(),\n",
    "            nn.MaxPool2d(kernel_size=2),\n",
    "            nn.Conv2d(in_channels=32, out_channels=32, kernel_size=3, padding=\"same\"),\n",
    "            nn.ReLU(),\n",
    "            nn.Conv2d(in_channels=32, out_channels=32, kernel_size=3, padding=\"same\"),\n",
    "            nn.ReLU(),\n",
    "            nn.MaxPool2d(kernel_size=2),\n",
    "            nn.Conv2d(in_channels=32, out_channels=32, kernel_size=3, padding=\"same\"),\n",
    "            nn.ReLU(),\n",
    "            nn.Conv2d(in_channels=32, out_channels=32, kernel_size=3, padding=\"same\"),\n",
    "            nn.ReLU(),\n",
    "            nn.MaxPool2d(kernel_size=2),\n",
    "            nn.Flatten(),\n",
    "            nn.Linear(512, num_classes),\n",
    "        )\n",
    "        self.init_weights()\n",
    "        \n",
    "    def init_weights(self):\n",
    "        \"\"\"使用 xavier 均匀分布来初始化全连接层、卷积层的权重 W\"\"\"\n",
    "        for m in self.modules():\n",
    "            if isinstance(m, (nn.Linear, nn.Conv2d)): # 全连接层、卷积层\n",
    "                nn.init.kaiming_uniform_(m.weight, mode='fan_in', nonlinearity='relu') # 采用kaiming_uniform初始化权重, mode='fan_in'表示权重初始化为均匀分布，nonlinearity='relu'表示激活函数为relu\n",
    "                if m.bias is not None:\n",
    "                    nn.init.constant_(m.bias, 0.01)  # 偏置初始化为常数值，比如0.01\n",
    "        \n",
    "    def forward(self, x):\n",
    "        return self.model(x)\n",
    "        \n",
    "for key, value in VGG(len(class_names)).named_parameters():\n",
    "    print(f\"{key:^40}paramerters num: {np.prod(value.shape)}\")\n",
    "    \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 训练\n",
    "\n",
    "pytorch的训练需要自行实现，包括\n",
    "1. 定义损失函数\n",
    "2. 定义优化器\n",
    "3. 定义训练步\n",
    "4. 训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-22T03:16:16.220932Z",
     "iopub.status.busy": "2025-01-22T03:16:16.220619Z",
     "iopub.status.idle": "2025-01-22T03:16:16.331938Z",
     "shell.execute_reply": "2025-01-22T03:16:16.331161Z",
     "shell.execute_reply.started": "2025-01-22T03:16:16.220910Z"
    }
   },
   "outputs": [],
   "source": [
    "from sklearn.metrics import accuracy_score\n",
    "\n",
    "@torch.no_grad()\n",
    "def evaluating(model, dataloader, loss_fct):\n",
    "    loss_list = []\n",
    "    pred_list = []\n",
    "    label_list = []\n",
    "    for datas, labels in dataloader:\n",
    "        datas = datas.to(device)\n",
    "        labels = labels.to(device)\n",
    "        # 前向计算\n",
    "        logits = model(datas)\n",
    "        loss = loss_fct(logits, labels)         # 验证集损失\n",
    "        loss_list.append(loss.item())\n",
    "        \n",
    "        preds = logits.argmax(axis=-1)    # 验证集预测\n",
    "        pred_list.extend(preds.cpu().numpy().tolist())\n",
    "        label_list.extend(labels.cpu().numpy().tolist())\n",
    "        \n",
    "    acc = accuracy_score(label_list, pred_list)\n",
    "    return np.mean(loss_list), acc\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### TensorBoard 可视化\n",
    "\n",
    "\n",
    "训练过程中可以使用如下命令启动tensorboard服务。\n",
    "\n",
    "```shell\n",
    "tensorboard \\\n",
    "    --logdir=runs \\     # log 存放路径\n",
    "    --host 0.0.0.0 \\    # ip\n",
    "    --port 8848         # 端口\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-22T03:16:16.333395Z",
     "iopub.status.busy": "2025-01-22T03:16:16.332836Z",
     "iopub.status.idle": "2025-01-22T03:16:16.540145Z",
     "shell.execute_reply": "2025-01-22T03:16:16.539490Z",
     "shell.execute_reply.started": "2025-01-22T03:16:16.333366Z"
    }
   },
   "outputs": [],
   "source": [
    "from torch.utils.tensorboard import SummaryWriter\n",
    "\n",
    "\n",
    "class TensorBoardCallback:\n",
    "    def __init__(self, log_dir, flush_secs=10):\n",
    "        \"\"\"\n",
    "        Args:\n",
    "            log_dir (str): dir to write log.\n",
    "            flush_secs (int, optional): write to dsk each flush_secs seconds. Defaults to 10.\n",
    "        \"\"\"\n",
    "        self.writer = SummaryWriter(log_dir=log_dir, flush_secs=flush_secs)\n",
    "\n",
    "    def draw_model(self, model, input_shape):\n",
    "        self.writer.add_graph(model, input_to_model=torch.randn(input_shape))\n",
    "        \n",
    "    def add_loss_scalars(self, step, loss, val_loss):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/loss\", \n",
    "            tag_scalar_dict={\"loss\": loss, \"val_loss\": val_loss},\n",
    "            global_step=step,\n",
    "            )\n",
    "        \n",
    "    def add_acc_scalars(self, step, acc, val_acc):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/accuracy\",\n",
    "            tag_scalar_dict={\"accuracy\": acc, \"val_accuracy\": val_acc},\n",
    "            global_step=step,\n",
    "        )\n",
    "        \n",
    "    def add_lr_scalars(self, step, learning_rate):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/learning_rate\",\n",
    "            tag_scalar_dict={\"learning_rate\": learning_rate},\n",
    "            global_step=step,\n",
    "            \n",
    "        )\n",
    "    \n",
    "    def __call__(self, step, **kwargs):\n",
    "        # add loss\n",
    "        loss = kwargs.pop(\"loss\", None)\n",
    "        val_loss = kwargs.pop(\"val_loss\", None)\n",
    "        if loss is not None and val_loss is not None:\n",
    "            self.add_loss_scalars(step, loss, val_loss)\n",
    "        # add acc\n",
    "        acc = kwargs.pop(\"acc\", None)\n",
    "        val_acc = kwargs.pop(\"val_acc\", None)\n",
    "        if acc is not None and val_acc is not None:\n",
    "            self.add_acc_scalars(step, acc, val_acc)\n",
    "        # add lr\n",
    "        learning_rate = kwargs.pop(\"lr\", None)\n",
    "        if learning_rate is not None:\n",
    "            self.add_lr_scalars(step, learning_rate)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Save Best\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-22T03:16:16.541372Z",
     "iopub.status.busy": "2025-01-22T03:16:16.540905Z",
     "iopub.status.idle": "2025-01-22T03:16:16.546777Z",
     "shell.execute_reply": "2025-01-22T03:16:16.546110Z",
     "shell.execute_reply.started": "2025-01-22T03:16:16.541350Z"
    }
   },
   "outputs": [],
   "source": [
    "class SaveCheckpointsCallback:\n",
    "    def __init__(self, save_dir, save_step=5000, save_best_only=True):\n",
    "        \"\"\"\n",
    "        Save checkpoints each save_epoch epoch. \n",
    "        We save checkpoint by epoch in this implementation.\n",
    "        Usually, training scripts with pytorch evaluating model and save checkpoint by step.\n",
    "\n",
    "        Args:\n",
    "            save_dir (str): dir to save checkpoint\n",
    "            save_epoch (int, optional): the frequency to save checkpoint. Defaults to 1.\n",
    "            save_best_only (bool, optional): If True, only save the best model or save each model at every epoch.\n",
    "        \"\"\"\n",
    "        self.save_dir = save_dir\n",
    "        self.save_step = save_step\n",
    "        self.save_best_only = save_best_only\n",
    "        self.best_metrics = -1\n",
    "        \n",
    "        # mkdir\n",
    "        if not os.path.exists(self.save_dir):\n",
    "            os.mkdir(self.save_dir)\n",
    "        \n",
    "    def __call__(self, step, state_dict, metric=None):\n",
    "        if step % self.save_step > 0:\n",
    "            return\n",
    "        \n",
    "        if self.save_best_only:\n",
    "            assert metric is not None\n",
    "            if metric >= self.best_metrics:\n",
    "                # save checkpoints\n",
    "                torch.save(state_dict, os.path.join(self.save_dir, \"best.ckpt\"))\n",
    "                # update best metrics\n",
    "                self.best_metrics = metric\n",
    "        else:\n",
    "            torch.save(state_dict, os.path.join(self.save_dir, f\"{step}.ckpt\"))\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Early Stop"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-22T03:16:16.547995Z",
     "iopub.status.busy": "2025-01-22T03:16:16.547503Z",
     "iopub.status.idle": "2025-01-22T03:16:16.552139Z",
     "shell.execute_reply": "2025-01-22T03:16:16.551591Z",
     "shell.execute_reply.started": "2025-01-22T03:16:16.547975Z"
    }
   },
   "outputs": [],
   "source": [
    "class EarlyStopCallback:\n",
    "    def __init__(self, patience=5, min_delta=0.01):\n",
    "        \"\"\"\n",
    "\n",
    "        Args:\n",
    "            patience (int, optional): Number of epochs with no improvement after which training will be stopped.. Defaults to 5.\n",
    "            min_delta (float, optional): Minimum change in the monitored quantity to qualify as an improvement, i.e. an absolute \n",
    "                change of less than min_delta, will count as no improvement. Defaults to 0.01.\n",
    "        \"\"\"\n",
    "        self.patience = patience\n",
    "        self.min_delta = min_delta\n",
    "        self.best_metric = -1\n",
    "        self.counter = 0\n",
    "        \n",
    "    def __call__(self, metric):\n",
    "        if metric >= self.best_metric + self.min_delta:\n",
    "            # update best metric\n",
    "            self.best_metric = metric\n",
    "            # reset counter \n",
    "            self.counter = 0\n",
    "        else: \n",
    "            self.counter += 1\n",
    "            \n",
    "    @property\n",
    "    def early_stop(self):\n",
    "        return self.counter >= self.patience\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-22T03:16:16.553245Z",
     "iopub.status.busy": "2025-01-22T03:16:16.552933Z",
     "iopub.status.idle": "2025-01-22T03:17:42.642456Z",
     "shell.execute_reply": "2025-01-22T03:17:42.641822Z",
     "shell.execute_reply.started": "2025-01-22T03:16:16.553223Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 7040/7040 [01:25<00:00, 82.19it/s, epoch=9] \n"
     ]
    }
   ],
   "source": [
    "# 训练\n",
    "def training(\n",
    "    model, \n",
    "    train_loader, \n",
    "    val_loader, \n",
    "    epoch, \n",
    "    loss_fct, \n",
    "    optimizer, \n",
    "    tensorboard_callback=None,\n",
    "    save_ckpt_callback=None,\n",
    "    early_stop_callback=None,\n",
    "    eval_step=500,\n",
    "    ):\n",
    "    record_dict = {\n",
    "        \"train\": [],\n",
    "        \"val\": []\n",
    "    }\n",
    "    \n",
    "    global_step = 0\n",
    "    model.train()\n",
    "    with tqdm(total=epoch * len(train_loader)) as pbar:\n",
    "        for epoch_id in range(epoch):\n",
    "            # training\n",
    "            for datas, labels in train_loader:\n",
    "                datas = datas.to(device)\n",
    "                labels = labels.to(device)\n",
    "                # 梯度清空\n",
    "                optimizer.zero_grad()\n",
    "                # 模型前向计算\n",
    "                logits = model(datas)\n",
    "                # 计算损失\n",
    "                loss = loss_fct(logits, labels)\n",
    "                # 梯度回传\n",
    "                loss.backward()\n",
    "                # 调整优化器，包括学习率的变动等\n",
    "                optimizer.step()\n",
    "                preds = logits.argmax(axis=-1)\n",
    "            \n",
    "                acc = accuracy_score(labels.cpu().numpy(), preds.cpu().numpy())    \n",
    "                loss = loss.cpu().item()\n",
    "                # record\n",
    "                \n",
    "                record_dict[\"train\"].append({\n",
    "                    \"loss\": loss, \"acc\": acc, \"step\": global_step\n",
    "                })\n",
    "                \n",
    "                # evaluating\n",
    "                if global_step % eval_step == 0:\n",
    "                    model.eval()\n",
    "                    val_loss, val_acc = evaluating(model, val_loader, loss_fct)\n",
    "                    record_dict[\"val\"].append({\n",
    "                        \"loss\": val_loss, \"acc\": val_acc, \"step\": global_step\n",
    "                    })\n",
    "                    model.train()\n",
    "                    \n",
    "                    # 1. 使用 tensorboard 可视化\n",
    "                    if tensorboard_callback is not None:\n",
    "                        tensorboard_callback(\n",
    "                            global_step, \n",
    "                            loss=loss, val_loss=val_loss,\n",
    "                            acc=acc, val_acc=val_acc,\n",
    "                            lr=optimizer.param_groups[0][\"lr\"],\n",
    "                            )\n",
    "                \n",
    "                    # 2. 保存模型权重 save model checkpoint\n",
    "                    if save_ckpt_callback is not None:\n",
    "                        save_ckpt_callback(global_step, model.state_dict(), metric=val_acc)\n",
    "\n",
    "                    # 3. 早停 Early Stop\n",
    "                    if early_stop_callback is not None:\n",
    "                        early_stop_callback(val_acc)\n",
    "                        if early_stop_callback.early_stop:\n",
    "                            print(f\"Early stop at epoch {epoch_id} / global_step {global_step}\")\n",
    "                            return record_dict\n",
    "                    \n",
    "                # udate step\n",
    "                global_step += 1\n",
    "                pbar.update(1)\n",
    "                pbar.set_postfix({\"epoch\": epoch_id})\n",
    "        \n",
    "    return record_dict\n",
    "        \n",
    "\n",
    "epoch = 10\n",
    "\n",
    "model = VGG(num_classes=10)\n",
    "\n",
    "# 1. 定义损失函数 采用交叉熵损失\n",
    "loss_fct = nn.CrossEntropyLoss()\n",
    "# 2. 定义优化器 采用 adam\n",
    "# Optimizers specified in the torch.optim package,可以修改beta1,beta2,weight_decay等参数\n",
    "optimizer = torch.optim.Adam(model.parameters(), lr=0.001,betas=(0.9,0.999))\n",
    "\n",
    "# 1. tensorboard 可视化\n",
    "if not os.path.exists(\"runs\"):\n",
    "    os.mkdir(\"runs\")\n",
    "tensorboard_callback = TensorBoardCallback(\"runs/vgg\")\n",
    "tensorboard_callback.draw_model(model, [1, 3, IMAGE_SIZE, IMAGE_SIZE])\n",
    "# 2. save best\n",
    "if not os.path.exists(\"checkpoints\"):\n",
    "    os.makedirs(\"checkpoints\")\n",
    "save_ckpt_callback = SaveCheckpointsCallback(\"checkpoints/vgg\", save_step=len(train_dl), save_best_only=True)\n",
    "# 3. early stop\n",
    "early_stop_callback = EarlyStopCallback(patience=5)\n",
    "\n",
    "model = model.to(device)\n",
    "record = training(\n",
    "    model, \n",
    "    train_dl, \n",
    "    eval_dl, \n",
    "    epoch, \n",
    "    loss_fct, \n",
    "    optimizer, \n",
    "    tensorboard_callback=tensorboard_callback,\n",
    "    save_ckpt_callback=save_ckpt_callback,\n",
    "    early_stop_callback=early_stop_callback,\n",
    "    eval_step=len(train_dl)\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-22T03:17:42.643588Z",
     "iopub.status.busy": "2025-01-22T03:17:42.643294Z",
     "iopub.status.idle": "2025-01-22T03:17:42.899420Z",
     "shell.execute_reply": "2025-01-22T03:17:42.898741Z",
     "shell.execute_reply.started": "2025-01-22T03:17:42.643565Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#画线要注意的是损失是不一定在零到1之间的\n",
    "def plot_learning_curves(record_dict, sample_step=500):\n",
    "    # build DataFrame\n",
    "    train_df = pd.DataFrame(record_dict[\"train\"]).set_index(\"step\").iloc[::sample_step]\n",
    "    val_df = pd.DataFrame(record_dict[\"val\"]).set_index(\"step\")\n",
    "\n",
    "    # plot\n",
    "    fig_num = len(train_df.columns)\n",
    "    fig, axs = plt.subplots(1, fig_num, figsize=(5 * fig_num, 5))\n",
    "    for idx, item in enumerate(train_df.columns):    \n",
    "        axs[idx].plot(train_df.index, train_df[item], label=f\"train_{item}\")\n",
    "        axs[idx].plot(val_df.index, val_df[item], label=f\"val_{item}\")\n",
    "        axs[idx].grid()\n",
    "        axs[idx].legend()\n",
    "        # axs[idx].set_xticks(range(0, train_df.index[-1], 5000))\n",
    "        # axs[idx].set_xticklabels(map(lambda x: f\"{int(x/1000)}k\", range(0, train_df.index[-1], 5000)))\n",
    "        axs[idx].set_xlabel(\"step\")\n",
    "    \n",
    "    plt.show()\n",
    "\n",
    "plot_learning_curves(record, sample_step=100)  #横坐标是 steps"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 评估"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-22T03:17:42.900453Z",
     "iopub.status.busy": "2025-01-22T03:17:42.900174Z",
     "iopub.status.idle": "2025-01-22T03:17:43.633854Z",
     "shell.execute_reply": "2025-01-22T03:17:43.633257Z",
     "shell.execute_reply.started": "2025-01-22T03:17:42.900432Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/ipykernel_557/244139278.py:4: FutureWarning: You are using `torch.load` with `weights_only=False` (the current default value), which uses the default pickle module implicitly. It is possible to construct malicious pickle data which will execute arbitrary code during unpickling (See https://github.com/pytorch/pytorch/blob/main/SECURITY.md#untrusted-models for more details). In a future release, the default value for `weights_only` will be flipped to `True`. This limits the functions that could be executed during unpickling. Arbitrary objects will no longer be allowed to be loaded via this mode unless they are explicitly allowlisted by the user via `torch.serialization.add_safe_globals`. We recommend you start setting `weights_only=True` for any use case where you don't have full control of the loaded file. Please open an issue on GitHub for any issues related to this experimental feature.\n",
      "  model.load_state_dict(torch.load(\"checkpoints/vgg/best.ckpt\", map_location=\"cpu\"))\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "loss:     0.9623\n",
      "accuracy: 0.6620\n"
     ]
    }
   ],
   "source": [
    "# dataload for evaluating\n",
    "\n",
    "# load checkpoints\n",
    "model.load_state_dict(torch.load(\"checkpoints/vgg/best.ckpt\", map_location=\"cpu\"))\n",
    "\n",
    "model.eval()\n",
    "loss, acc = evaluating(model, eval_dl, loss_fct)\n",
    "print(f\"loss:     {loss:.4f}\\naccuracy: {acc:.4f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 推理"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "ExecutionIndicator": {
     "show": false
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T03:17:43.634832Z",
     "iopub.status.busy": "2025-01-22T03:17:43.634562Z",
     "iopub.status.idle": "2025-01-22T03:20:28.425230Z",
     "shell.execute_reply": "2025-01-22T03:20:28.424309Z",
     "shell.execute_reply.started": "2025-01-22T03:17:43.634810Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      " 93%|█████████▎| 4379/4688 [02:44<00:11, 26.66it/s]\n"
     ]
    },
    {
     "ename": "KeyboardInterrupt",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
      "\u001B[0;31mKeyboardInterrupt\u001B[0m                         Traceback (most recent call last)",
      "Cell \u001B[0;32mIn[15], line 7\u001B[0m\n\u001B[1;32m      5\u001B[0m preds_collect \u001B[38;5;241m=\u001B[39m []\n\u001B[1;32m      6\u001B[0m model\u001B[38;5;241m.\u001B[39meval()\n\u001B[0;32m----> 7\u001B[0m \u001B[38;5;28;01mfor\u001B[39;00m data, fake_label \u001B[38;5;129;01min\u001B[39;00m tqdm(test_dl):\n\u001B[1;32m      8\u001B[0m     data \u001B[38;5;241m=\u001B[39m data\u001B[38;5;241m.\u001B[39mto(device\u001B[38;5;241m=\u001B[39mdevice)\n\u001B[1;32m      9\u001B[0m     logits \u001B[38;5;241m=\u001B[39m model(data)\n",
      "File \u001B[0;32m/usr/local/lib/python3.10/site-packages/tqdm/std.py:1181\u001B[0m, in \u001B[0;36mtqdm.__iter__\u001B[0;34m(self)\u001B[0m\n\u001B[1;32m   1178\u001B[0m time \u001B[38;5;241m=\u001B[39m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m_time\n\u001B[1;32m   1180\u001B[0m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[0;32m-> 1181\u001B[0m     \u001B[38;5;28;01mfor\u001B[39;00m obj \u001B[38;5;129;01min\u001B[39;00m iterable:\n\u001B[1;32m   1182\u001B[0m         \u001B[38;5;28;01myield\u001B[39;00m obj\n\u001B[1;32m   1183\u001B[0m         \u001B[38;5;66;03m# Update and possibly print the progressbar.\u001B[39;00m\n\u001B[1;32m   1184\u001B[0m         \u001B[38;5;66;03m# Note: does not call self.update(1) for speed optimisation.\u001B[39;00m\n",
      "File \u001B[0;32m/usr/local/lib/python3.10/site-packages/torch/utils/data/dataloader.py:701\u001B[0m, in \u001B[0;36m_BaseDataLoaderIter.__next__\u001B[0;34m(self)\u001B[0m\n\u001B[1;32m    698\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m_sampler_iter \u001B[38;5;129;01mis\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m:\n\u001B[1;32m    699\u001B[0m     \u001B[38;5;66;03m# TODO(https://github.com/pytorch/pytorch/issues/76750)\u001B[39;00m\n\u001B[1;32m    700\u001B[0m     \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m_reset()  \u001B[38;5;66;03m# type: ignore[call-arg]\u001B[39;00m\n\u001B[0;32m--> 701\u001B[0m data \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m_next_data\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m    702\u001B[0m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m_num_yielded \u001B[38;5;241m+\u001B[39m\u001B[38;5;241m=\u001B[39m \u001B[38;5;241m1\u001B[39m\n\u001B[1;32m    703\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m (\n\u001B[1;32m    704\u001B[0m     \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m_dataset_kind \u001B[38;5;241m==\u001B[39m _DatasetKind\u001B[38;5;241m.\u001B[39mIterable\n\u001B[1;32m    705\u001B[0m     \u001B[38;5;129;01mand\u001B[39;00m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m_IterableDataset_len_called \u001B[38;5;129;01mis\u001B[39;00m \u001B[38;5;129;01mnot\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m\n\u001B[1;32m    706\u001B[0m     \u001B[38;5;129;01mand\u001B[39;00m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m_num_yielded \u001B[38;5;241m>\u001B[39m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m_IterableDataset_len_called\n\u001B[1;32m    707\u001B[0m ):\n",
      "File \u001B[0;32m/usr/local/lib/python3.10/site-packages/torch/utils/data/dataloader.py:757\u001B[0m, in \u001B[0;36m_SingleProcessDataLoaderIter._next_data\u001B[0;34m(self)\u001B[0m\n\u001B[1;32m    755\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m\u001B[38;5;250m \u001B[39m\u001B[38;5;21m_next_data\u001B[39m(\u001B[38;5;28mself\u001B[39m):\n\u001B[1;32m    756\u001B[0m     index \u001B[38;5;241m=\u001B[39m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m_next_index()  \u001B[38;5;66;03m# may raise StopIteration\u001B[39;00m\n\u001B[0;32m--> 757\u001B[0m     data \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m_dataset_fetcher\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mfetch\u001B[49m\u001B[43m(\u001B[49m\u001B[43mindex\u001B[49m\u001B[43m)\u001B[49m  \u001B[38;5;66;03m# may raise StopIteration\u001B[39;00m\n\u001B[1;32m    758\u001B[0m     \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m_pin_memory:\n\u001B[1;32m    759\u001B[0m         data \u001B[38;5;241m=\u001B[39m _utils\u001B[38;5;241m.\u001B[39mpin_memory\u001B[38;5;241m.\u001B[39mpin_memory(data, \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m_pin_memory_device)\n",
      "File \u001B[0;32m/usr/local/lib/python3.10/site-packages/torch/utils/data/_utils/fetch.py:52\u001B[0m, in \u001B[0;36m_MapDatasetFetcher.fetch\u001B[0;34m(self, possibly_batched_index)\u001B[0m\n\u001B[1;32m     50\u001B[0m         data \u001B[38;5;241m=\u001B[39m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mdataset\u001B[38;5;241m.\u001B[39m__getitems__(possibly_batched_index)\n\u001B[1;32m     51\u001B[0m     \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[0;32m---> 52\u001B[0m         data \u001B[38;5;241m=\u001B[39m [\u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mdataset[idx] \u001B[38;5;28;01mfor\u001B[39;00m idx \u001B[38;5;129;01min\u001B[39;00m possibly_batched_index]\n\u001B[1;32m     53\u001B[0m \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[1;32m     54\u001B[0m     data \u001B[38;5;241m=\u001B[39m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mdataset[possibly_batched_index]\n",
      "File \u001B[0;32m/usr/local/lib/python3.10/site-packages/torch/utils/data/_utils/fetch.py:52\u001B[0m, in \u001B[0;36m<listcomp>\u001B[0;34m(.0)\u001B[0m\n\u001B[1;32m     50\u001B[0m         data \u001B[38;5;241m=\u001B[39m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mdataset\u001B[38;5;241m.\u001B[39m__getitems__(possibly_batched_index)\n\u001B[1;32m     51\u001B[0m     \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[0;32m---> 52\u001B[0m         data \u001B[38;5;241m=\u001B[39m [\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mdataset\u001B[49m\u001B[43m[\u001B[49m\u001B[43midx\u001B[49m\u001B[43m]\u001B[49m \u001B[38;5;28;01mfor\u001B[39;00m idx \u001B[38;5;129;01min\u001B[39;00m possibly_batched_index]\n\u001B[1;32m     53\u001B[0m \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[1;32m     54\u001B[0m     data \u001B[38;5;241m=\u001B[39m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mdataset[possibly_batched_index]\n",
      "Cell \u001B[0;32mIn[4], line 22\u001B[0m, in \u001B[0;36mCifar10Dataset.__getitem__\u001B[0;34m(self, index)\u001B[0m\n\u001B[1;32m     20\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m\u001B[38;5;250m \u001B[39m\u001B[38;5;21m__getitem__\u001B[39m(\u001B[38;5;28mself\u001B[39m, index):\n\u001B[1;32m     21\u001B[0m     img_path, label \u001B[38;5;241m=\u001B[39m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mdf\u001B[38;5;241m.\u001B[39miloc[index]\n\u001B[0;32m---> 22\u001B[0m     img \u001B[38;5;241m=\u001B[39m \u001B[43mImage\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mopen\u001B[49m\u001B[43m(\u001B[49m\u001B[43mimg_path\u001B[49m\u001B[43m)\u001B[49m\u001B[38;5;241m.\u001B[39mconvert(\u001B[38;5;124m'\u001B[39m\u001B[38;5;124mRGB\u001B[39m\u001B[38;5;124m'\u001B[39m)\n\u001B[1;32m     23\u001B[0m     \u001B[38;5;66;03m# # img 转换为 channel first\u001B[39;00m\n\u001B[1;32m     24\u001B[0m     \u001B[38;5;66;03m# img = img.transpose((2, 0, 1))\u001B[39;00m\n\u001B[1;32m     25\u001B[0m     \u001B[38;5;66;03m# transform\u001B[39;00m\n\u001B[1;32m     26\u001B[0m     img \u001B[38;5;241m=\u001B[39m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mtransform(img)\n",
      "File \u001B[0;32m/usr/local/lib/python3.10/site-packages/PIL/Image.py:3442\u001B[0m, in \u001B[0;36mopen\u001B[0;34m(fp, mode, formats)\u001B[0m\n\u001B[1;32m   3439\u001B[0m     fp \u001B[38;5;241m=\u001B[39m io\u001B[38;5;241m.\u001B[39mBytesIO(fp\u001B[38;5;241m.\u001B[39mread())\n\u001B[1;32m   3440\u001B[0m     exclusive_fp \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;01mTrue\u001B[39;00m\n\u001B[0;32m-> 3442\u001B[0m prefix \u001B[38;5;241m=\u001B[39m \u001B[43mfp\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mread\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;241;43m16\u001B[39;49m\u001B[43m)\u001B[49m\n\u001B[1;32m   3444\u001B[0m preinit()\n\u001B[1;32m   3446\u001B[0m warning_messages: \u001B[38;5;28mlist\u001B[39m[\u001B[38;5;28mstr\u001B[39m] \u001B[38;5;241m=\u001B[39m []\n",
      "\u001B[0;31mKeyboardInterrupt\u001B[0m: "
     ]
    }
   ],
   "source": [
    "# test_df\n",
    "# test_ds = Cifar10Dataset(\"test\", transform=transforms_eval)\n",
    "# test_dl = DataLoader(test_ds, batch_size=batch_size, shuffle=False, drop_last=False)\n",
    "\n",
    "# preds_collect = []\n",
    "# model.eval()\n",
    "# for data, fake_label in tqdm(test_dl):\n",
    "#     data = data.to(device=device)\n",
    "#     logits = model(data)\n",
    "#     preds = [test_ds.idx_to_label[idx] for idx in logits.argmax(axis=-1).cpu().tolist()]\n",
    "#     preds_collect.extend(preds)\n",
    "    \n",
    "# test_df[\"class\"] = preds_collect\n",
    "# test_df.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "execution": {
     "iopub.status.busy": "2025-01-22T03:20:28.426136Z",
     "iopub.status.idle": "2025-01-22T03:20:28.426478Z",
     "shell.execute_reply": "2025-01-22T03:20:28.426320Z",
     "shell.execute_reply.started": "2025-01-22T03:20:28.426308Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# 导出 submission.csv\n",
    "# test_df.to_csv(\"submission.csv\", index=False)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
